package com.hanxiaozhang.result;


import com.hanxiaozhang.exception.BusinessException;
import com.hanxiaozhang.exception.BusinessExceptionEnum;
import com.hanxiaozhang.util.RequestContextUtil;
import com.hanxiaozhang.util.StringUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.http.HttpStatus;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 功能描述: <br>
 * 〈正常返回结果〉
 *
 * @Author:hanxiaozhang
 * @Date: 2020/7/21
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Result<T> implements Serializable {

    private static final long serialVersionUID = 874200365941306385L;

    /**
     * 编码
     */
    private Integer code;

    /**
     * 信息
     */
    private String msg;

    /**
     * 时间戳
     */
    private long time;


    /**
     * 返回数据
     */
    private T data;

    public Result(T data) {
        this.code = ResultCode.SUCCESS.code();
        this.msg = ResultCode.SUCCESS.message();
        this.data = data;
    }

    public Result(ResultCode resultCode, T data) {
        this.code = resultCode.code();
        this.msg = resultCode.message();
        this.data = data;
    }

    public static Result success() {
        Result result = new Result();
        result.setResultCode(ResultCode.SUCCESS);
        return result;
    }

    public static <T> Result success(T data) {
        Result result = new Result();
        result.setResultCode(ResultCode.SUCCESS);
        result.setData(data);
        return result;
    }

    public static Result error(ResultCode resultCode) {
        Result result = new Result();
        result.setResultCode(resultCode);
        return result;
    }

    public static <T> Result error(ResultCode resultCode, T data) {
        Result result = new Result();
        result.setResultCode(resultCode);
        result.setData(data);
        return result;
    }

    public static Result error(Integer code, String msg) {
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    public static <T> Result error(Integer code, String msg, T data) {
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }


    public static Result error(BusinessException e) {
        BusinessExceptionEnum ee = BusinessExceptionEnum.getByEClass(e.getClass());
        if (ee != null) {
            return Result.error(ee.getResultCode(), e, ee.getHttpStatus(), e.getData());
        }

        Result result = Result.error(e.getResultCode() == null ? ResultCode.NO_DEFINITION : e.getResultCode(), e, HttpStatus.OK, e.getData());

        if (StringUtil.isNotBlank(e.getMessage())) {
            result.setMsg(e.getMessage());
        }

        return result;
    }

    public static Result error(ResultCode resultCode, Throwable e, HttpStatus httpStatus) {

        Map<String, Object> map = new LinkedHashMap<>(8);
        map.put("status", httpStatus.value());
        map.put("error", httpStatus.getReasonPhrase());
        map.put("exception", e.getClass().getName());
        map.put("exceptionMsg", e.getMessage());
        map.put("cause", e.getCause() == null ? null : e.getCause().toString());
        map.put("path", RequestContextUtil.getRequest() == null ? null : RequestContextUtil.getRequest().getRequestURI());

        Result<Map<String, Object>> result = new Result<>();
        result.setCode(resultCode.code())
                .setMsg(resultCode.message())
                .setData(map);
        return result;
    }

    public static <T> Result error(ResultCode resultCode, Throwable e, HttpStatus httpStatus, T errorMsg) {
        Result result = Result.error(resultCode, e, httpStatus);
        Map<String, Object> map = (Map<String, Object>) result.getData();
        map.put("exceptionMsg", errorMsg);
        return result;
    }


    private void setResultCode(ResultCode code) {
        this.code = code.code();
        this.msg = code.message();
    }

    public long getTime() {
        return time == 0 ? System.currentTimeMillis() : time;
    }


}
